package cn.iocoder.yudao.module.digital.controller.admin.cardkeyproduct;

import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.digital.controller.admin.cardkeyproduct.vo.CardKeyProductImportExcelVO;
import cn.iocoder.yudao.module.digital.controller.admin.cardkeyproduct.vo.CardkeyProductPageReqVO;
import cn.iocoder.yudao.module.digital.controller.admin.cardkeyproduct.vo.CardkeyProductRespVO;
import cn.iocoder.yudao.module.digital.controller.admin.cardkeyproduct.vo.CardkeyProductSaveReqVO;
import cn.iocoder.yudao.module.digital.dal.dataobject.cardkeyproduct.CardkeyProductDO;
import cn.iocoder.yudao.module.digital.dal.dataobject.cardkeyproduct.CardkeyProductJoinDO;
import cn.iocoder.yudao.module.digital.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.digital.enums.cardkeysecret.CardKeySourceEnum;
import cn.iocoder.yudao.module.digital.service.cardchannel.ICardSecretManager;
import cn.iocoder.yudao.module.digital.service.cardkeyproduct.CardkeyProductService;
import cn.iocoder.yudao.module.digital.service.cardkeysecret.CardkeySecretService;
import cn.iocoder.yudao.module.product.enums.spu.CurrencyUnitEnum;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.IMPORT;

@Tag(name = "管理后台 - 卡密商品")
@RestController
@RequestMapping("/digital/cardkey-product")
@Validated
@Slf4j
public class CardkeyProductController {

    @Resource
    private CardkeyProductService cardkeyProductService;
    @Resource
    private CardkeySecretService cardkeySecretService;
    @Resource
    private ICardSecretManager cardSecretManager;

    @PostMapping("/create")
    @Operation(summary = "创建卡密商品")
    @PreAuthorize("@ss.hasPermission('digital:cardkey-product:create')")
    public CommonResult<Long> createCardkeyProduct(@Valid @RequestBody CardkeyProductSaveReqVO createReqVO) {
        return success(cardkeyProductService.createCardkeyProduct(createReqVO));
    }

    @PutMapping("/update")
    @Operation(summary = "更新卡密商品")
    @PreAuthorize("@ss.hasPermission('digital:cardkey-product:update')")
    public CommonResult<Boolean> updateCardkeyProduct(@Valid @RequestBody CardkeyProductSaveReqVO updateReqVO) {
        cardkeyProductService.updateCardkeyProduct(updateReqVO);
        return success(true);
    }

    @DeleteMapping("/delete")
    @Operation(summary = "删除卡密商品")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('digital:cardkey-product:delete')")
    public CommonResult<Boolean> deleteCardkeyProduct(@RequestParam("id") Long id) {
        cardkeyProductService.deleteCardkeyProduct(id);
        return success(true);
    }

    @GetMapping("/get")
    @Operation(summary = "获得卡密商品")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('digital:cardkey-product:query')")
    public CommonResult<CardkeyProductRespVO> getCardkeyProduct(@RequestParam("id") Long id) {
        CardkeyProductDO cardkeyProduct = cardkeyProductService.getCardkeyProduct(id);
        return success(BeanUtils.toBean(cardkeyProduct, CardkeyProductRespVO.class));
    }


    @GetMapping("/get-by-no")
    @Operation(summary = "获得卡密商品-通过CDK编号")
    @Parameter(name = "productNo", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('digital:cardkey-product:query')")
    public CommonResult<CardkeyProductRespVO> getCardkeyProductByNo(@RequestParam("productNo") String productNo) {
        CardkeyProductDO cardkeyProduct = cardkeyProductService.getCardKeyProductByNo(productNo);
        Long stock = cardkeySecretService.getStock(productNo);
        CardkeyProductRespVO vo = BeanUtils.toBean(cardkeyProduct, CardkeyProductRespVO.class);
        vo.setStock(stock.intValue());
        return success(vo);
    }

    @GetMapping("/page")
    @Operation(summary = "获得卡密商品分页")
    @PreAuthorize("@ss.hasPermission('digital:cardkey-product:query')")
    public CommonResult<PageResult<CardkeyProductRespVO>> getCardkeyProductPage(@Valid CardkeyProductPageReqVO pageReqVO) {
        PageResult<CardkeyProductJoinDO> pageResult = cardkeyProductService.getCardkeyProductPage(pageReqVO);
        return success(BeanUtils.toBean(pageResult, CardkeyProductRespVO.class));
    }

    @GetMapping("/export-excel")
    @Operation(summary = "导出卡密商品 Excel")
    @PreAuthorize("@ss.hasPermission('digital:cardkey-product:export')")
    @OperateLog(type = EXPORT)
    public void exportCardkeyProductExcel(@Valid CardkeyProductPageReqVO pageReqVO,
                                          HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        List<CardkeyProductJoinDO> list = cardkeyProductService.getCardkeyProductPage(pageReqVO).getList();
        if (CollUtil.isNotEmpty(list)) {
            list.forEach(v -> {
                // 将分转换为元
                if (v.getDenominationMin() != null) {
                    v.setDenominationMin(new BigDecimal(v.getDenominationMin()).divide(new BigDecimal(100)).intValue());
                }
                if (v.getDenominationMax() != null) {
                    v.setDenominationMax(new BigDecimal(v.getDenominationMax()).divide(new BigDecimal(100)).intValue());
                }
                if (v.getDenominationFixed() != null) {
                    v.setDenominationFixed(new BigDecimal(v.getDenominationFixed()).divide(new BigDecimal(100)).intValue());
                }
            });
        }
        // 导出 Excel
        ExcelUtils.write(response, "卡密商品.xls", "数据", CardkeyProductRespVO.class,
                BeanUtils.toBean(list, CardkeyProductRespVO.class));
    }

    @PostMapping("/import-excel")
    @Operation(summary = "导入卡密商品 Excel")
    @PreAuthorize("@ss.hasPermission('digital:cardkey-product:import')")
    @OperateLog(type = IMPORT)
    public CommonResult importCardkeyProductExcel(@RequestParam("file") MultipartFile file) throws IOException {
        // 检查文件是否为空
        if (file.isEmpty()) {
            throw new IllegalArgumentException("文件不能为空");
        }

        // 解析Excel文件
        final List<CardKeyProductImportExcelVO> list = ExcelUtils.read(file, CardKeyProductImportExcelVO.class);

        // 校验Excel数据
        validateCardKeyProductImportExcel(list);

        // 导入到数据库
        cardkeyProductService.importCardKeyProductAndSecret(list);

        return success(null);
    }

    // 校验Excel数据
    private static void validateCardKeyProductImportExcel(List<CardKeyProductImportExcelVO> list) {
        if (CollUtil.isEmpty(list)) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CARDKEY_DEFAULT_ERROR, "excel为空");
        }

        // 校验非空
        validateNullData(list);

        // 校验productNo相同的CardKeyProductImportExcelVO的productName/currencyCode/denominationFixed都必须相同
        validateRepeatData(list);
    }

    // 校验非空
    private static void validateNullData(List<CardKeyProductImportExcelVO> list) {
        int row = 0;
        for (CardKeyProductImportExcelVO vo : list) {
            row++;
            if (StringUtil.isEmpty(vo.getProductNo())) {
                log.warn("导入卡密商品及密钥-CDK编号为空, row = {}", row);
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.CARDKEY_DEFAULT_ERROR, String.format("第[%s]行的[CDK编号]为空", row));
            }
            if (StringUtil.isEmpty(vo.getProductName())) {
                log.warn("导入卡密商品及密钥-CDK名称为空, row = {}", row);
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.CARDKEY_DEFAULT_ERROR, String.format("第[%s]行的[CDK名称]为空", row));
            }
            if (StringUtil.isEmpty(vo.getCurrencyCode())) {
                log.warn("导入卡密商品及密钥-货币为空, row = {}", row);
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.CARDKEY_DEFAULT_ERROR, String.format("第[%s]行的[货币]为空", row));
            }
            try {
                CurrencyUnitEnum.fromValue(vo.getCurrencyCode()); // 检查货币代码
            } catch (Exception e) {
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.CARDKEY_DEFAULT_ERROR, e.getMessage());
            }

            if (vo.getDenominationFixed() == null) {
                log.warn("导入卡密商品及密钥-商品面值为空, row = {}", row);
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.CARDKEY_DEFAULT_ERROR, String.format("第[%s]行的[商品面值]为空", row));
            }
            if (vo.getCostPrice() == null) {
                log.warn("导入卡密商品及密钥-成本为空, row = {}", row);
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.CARDKEY_DEFAULT_ERROR, String.format("第[%s]行的[成本]为空", row));
            }
            if (StringUtil.isEmpty(vo.getCardNumber())) {
                log.warn("导入卡密商品及密钥-卡号为空, row = {}", row);
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.CARDKEY_DEFAULT_ERROR, String.format("第[%s]行的[卡号]为空", row));
            }
        }
    }

    // 校验productNo相同的CardKeyProductImportExcelVO的productName/currencyCode/denominationFixed都必须相同
    public static boolean validateRepeatData(List<CardKeyProductImportExcelVO> list) {
        final Map<String, CardKeyProductImportExcelVO> map = new HashMap<>();
        int row = 0;
        for (CardKeyProductImportExcelVO vo : list) {
            row++;
            final String productNo = vo.getProductNo();
            if (map.containsKey(productNo)) {
                final CardKeyProductImportExcelVO existingVO = map.get(productNo);
                if (!existingVO.getProductName().equals(vo.getProductName())) {
                    log.warn("导入卡密商品及密钥-请检查相同CDK编号存在CDK名称不一致的情况, row = {}, oldValue = {}, newValue = {}",
                            row, existingVO.getProductName(), vo.getProductName());
                    throw ServiceExceptionUtil.exception(ErrorCodeConstants.CARDKEY_DEFAULT_ERROR, String.format("请检查第[%s]行数据，存在CDK编号相同，但是[CDK名称]不一致的情况", row));
                }

                if (!existingVO.getCurrencyCode().equals(vo.getCurrencyCode())) {
                    log.warn("导入卡密商品及密钥-请检查相同CDK编号存在货币不一致的情况, row = {}, oldValue = {}, newValue = {}",
                            row, existingVO.getCurrencyCode(), vo.getCurrencyCode());
                    throw ServiceExceptionUtil.exception(ErrorCodeConstants.CARDKEY_DEFAULT_ERROR, String.format("请检查第[%s]行数据，存在CDK编号相同，但是[货币]不一致的情况", row));
                }

                if (!existingVO.getDenominationFixed().equals(vo.getDenominationFixed())) {
                    log.warn("导入卡密商品及密钥-请检查相同CDK编号存在商品面值不一致的情况, row = {}, oldValue = {}, newValue = {}",
                            row, existingVO.getDenominationFixed(), vo.getDenominationFixed());
                    throw ServiceExceptionUtil.exception(ErrorCodeConstants.CARDKEY_DEFAULT_ERROR, String.format("请检查第[%s]行数据，存在CDK编号相同，但是[商品面值]不一致的情况", row));
                }
            } else {
                map.put(productNo, vo);
            }
        }
        return true;
    }

    @GetMapping("/syn-product-from-api/{source}")
    @Operation(summary = "从api拉取卡密商品【内部使用】")
    @PreAuthorize("@ss.hasPermission('digital:cardkey-product:update')")
    public CommonResult synCardKeyProductFromApi(@PathVariable Integer source) {
        final CardKeySourceEnum sourceEnum = CardKeySourceEnum.fromValue(source);
        cardSecretManager.synAllProducts(sourceEnum);
        return success(null);
    }

}